﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Styx;
using Styx.WoWInternals.WoWObjects;
using Styx.Common;
using Styx.CommonBot.POI;
using Styx.WoWInternals;
using Styx.CommonBot;
using System.Runtime.Remoting.Contexts;
using System.Text.RegularExpressions;

namespace DestroLock
{
    internal static class GetTargets
    {
        public static void EnsureTarget()
        {
            var target = grabTarget();

            if (target != null)
            {
                if (target.Guid != StyxWoW.Me.CurrentTargetGuid)
                {
                    target.Target();
                    TankManager.TargetingTimer.Reset();
                }
                return;
            }
            if (target == null)
            {
                var target2 = grabTarget2();
                if (target2 != null && target2.Guid != StyxWoW.Me.CurrentTargetGuid)
                {
                    target2.Target();
                }
            }
        }

        public static WoWUnit grabTarget()
        {
            if (StyxWoW.Me.Shapeshift == ShapeshiftForm.Bear && MeIsTank && StyxWoW.Me.Combat &&
                TankManager.Instance.FirstUnit != null)
            {
                if (StyxWoW.Me.CurrentTarget != TankManager.Instance.FirstUnit)
                {
                    if (TankManager.TargetingTimer.IsFinished)
                    {
                        Logging.Write("TankTarget: switching to first unit of TankTargeting");
                        return TankManager.Instance.FirstUnit;
                    }

                    if (!Unit.ValidUnit(StyxWoW.Me.CurrentTarget))
                    {
                        Logging.Write("TankTarget: CurrentTarget invalid, switching to first unit of TankTargeting");
                        return TankManager.Instance.FirstUnit;
                    }
                }
                return StyxWoW.Me.CurrentTarget;
            }
            WoWUnit botpoi;
            if (BotPoi.Current.Type == PoiType.Kill)
            {
                botpoi = BotPoi.Current.AsObject.ToUnit();
                if (Unit.ValidUnit(botpoi))
                {
                    if (StyxWoW.Me.CurrentTargetGuid != botpoi.Guid)
                        Logging.Write("Switching to BotPoi: " + botpoi.SafeName + "!");

                    return botpoi;
                }
                BotPoi.Clear();
            }
            if (StyxWoW.Me.CurrentTarget == null || StyxWoW.Me.CurrentTarget.IsDead)
                return null;
            if (!StyxWoW.Me.IsInGroup() && StyxWoW.Me.Combat &&
                ((!StyxWoW.Me.CurrentTarget.Combat && !StyxWoW.Me.CurrentTarget.Aggro &&
                  !StyxWoW.Me.CurrentTarget.PetAggro) || StyxWoW.Me.CurrentTarget.Distance > 30 ||
                 !StyxWoW.Me.CurrentTarget.InLineOfSpellSight))
            {
                // Look for agrroed mobs next. prioritize by IsPlayer, Relative Distance, then Health
                var target = ObjectManager.GetObjectsOfType<WoWUnit>(false, false)
                    .Where(
                        p => p.Distance < 10
                             && Unit.ValidUnit(p)
                             && (p.Aggro || p.PetAggro)
                             && p.InLineOfSpellSight
                    )
                    // .OrderBy(u => CalcDistancePriority(u)).ThenBy(u => u.HealthPercent)
                    .OrderBy(u => u.HealthPercent)
                    .FirstOrDefault();

                if (target != null && target.Guid != StyxWoW.Me.CurrentTargetGuid)
                {
                    // Return the closest one to us
                    Logging.Write("Switching to aggroed mob pounding on me " + target.SafeName + "!");
                    return target;
                }
            }
            var pOwner = Unit.GetPlayerParent(StyxWoW.Me.CurrentTarget);
            if (pOwner != null && Unit.ValidUnit(pOwner) && !Blacklist.Contains(pOwner, BlacklistFlags.Combat))
            {
                Logging.Write("Current target owned by a player.  Switching to " + pOwner.SafeName + "!");
                if (BotPoi.Current.Type == PoiType.Kill && BotPoi.Current.Guid == StyxWoW.Me.CurrentTarget.Guid)
                    BotPoi.Clear(string.Format("Superbad detected {0} as Player Owned Pet",
                        StyxWoW.Me.CurrentTarget.SafeName));

                return pOwner;
            }
            if (Blacklist.Contains(StyxWoW.Me.CurrentTargetGuid, BlacklistFlags.Combat))
            {
                if (StyxWoW.Me.CurrentTarget.Combat && StyxWoW.Me.CurrentTarget.IsTargetingMeOrPet)
                {
                    Logging.Write("Current target " + StyxWoW.Me.CurrentTarget.SafeName +
                                  " blacklisted and Bot has no other targets!  Fighting this one and hoping Bot wakes up if its Evade bugged!");
                    return StyxWoW.Me.CurrentTarget;
                }

                Logging.Write("CurrentTarget " + StyxWoW.Me.CurrentTarget.SafeName +
                              " blacklisted and not in combat with so clearing target!");
                StyxWoW.Me.ClearTarget();
                return null;
            }

            if (Unit.ValidUnit(StyxWoW.Me.CurrentTarget))
                return StyxWoW.Me.CurrentTarget;

            // at this point, stick with it if in Targetlist
            if (Targeting.Instance.TargetList.Contains(StyxWoW.Me.CurrentTarget))
            {
                Logging.Write("EnsureTarget: failed validation but {0} is in TargetList, continuing...",
                    StyxWoW.Me.CurrentTarget.SafeName);
                return StyxWoW.Me.CurrentTarget;
            }

            if (StyxWoW.Me.CurrentTarget.SafeName == "Dragonmaw War Banner" || StyxWoW.Me.CurrentTarget.SafeName == "Healing Tide Totem")
                return StyxWoW.Me.CurrentTarget;

            // otherwise, let's get a new one
            Logging.Write("EnsureTarget: invalid target {0}, so forcing selection of a new one...",
                StyxWoW.Me.CurrentTarget == null ? "(null)" : StyxWoW.Me.CurrentTarget.SafeName);
            return null;
        }

        public static WoWUnit grabTarget2()
        {
            var rafLeader = RaFHelper.Leader;
            if (rafLeader != null && rafLeader.IsValid && !rafLeader.IsMe && rafLeader.Combat &&
                rafLeader.CurrentTarget != null && rafLeader.CurrentTarget.IsAlive &&
                !Blacklist.Contains(rafLeader.CurrentTarget, BlacklistFlags.Combat))
            {
                Logging.Write("Current target invalid. Switching to Tanks target " + rafLeader.CurrentTarget.SafeName +
                              "!");
                return rafLeader.CurrentTarget;
            }

            // if we have BotPoi then try it
            if (Context.KittyRoutine.CurrentWoWContext != WoWContext.Normal && BotPoi.Current.Type == PoiType.Kill)
            {
                var unit = BotPoi.Current.AsObject as WoWUnit;
                if (unit == null)
                {
                    Logging.Write("Current Kill POI invalid. Clearing POI!");
                    BotPoi.Clear();
                }
                else if (!unit.IsAlive)
                {
                    Logging.Write("Current Kill POI dead. Clearing POI " + unit.SafeName + "!");
                    BotPoi.Clear();
                }
                else if (Blacklist.Contains(unit, BlacklistFlags.Combat))
                {
                    Logging.Write("Current Kill POI is blacklisted. Clearing POI " + unit.SafeName + "!");
                    BotPoi.Clear();
                }
                else
                {
                    Logging.Write("Current target invalid. Switching to POI " + unit.SafeName + "!");
                    return unit;
                }
            }

            // Look for agrroed mobs next. prioritize by IsPlayer, Relative Distance, then Health
            var target = Targeting.Instance.TargetList
                .Where(
                    p => !Blacklist.Contains(p, BlacklistFlags.Combat)
                         && Unit.ValidUnit(p)
                        // && p.DistanceSqr <= 40 * 40  // dont restrict check to 40 yds
                         &&
                         (p.Aggro || p.PetAggro ||
                          (p.Combat && p.GotTarget && (p.IsTargetingMeOrPet || p.IsTargetingMyRaidMember))))
                .OrderBy(u => u.IsPlayer)
                .ThenBy(u => CalcDistancePriority(u))
                .ThenBy(u => u.HealthPercent)
                .FirstOrDefault();

            if (target != null)
            {
                // Return the closest one to us
                Logging.Write("Current target invalid. Switching to aggroed mob " + target.SafeName + "!");
                return target;
            }

            // if we have BotPoi then try it
            if (Context.KittyRoutine.CurrentWoWContext == WoWContext.Normal && BotPoi.Current.Type == PoiType.Kill)
            {
                var unit = BotPoi.Current.AsObject as WoWUnit;
                if (unit == null)
                {
                    Logging.Write("Current Kill POI invalid. Clearing POI!");
                    BotPoi.Clear();
                }
                else if (!unit.IsAlive)
                {
                    Logging.Write("Current Kill POI dead. Clearing POI " + unit.SafeName + "!");
                    BotPoi.Clear();
                }
                else if (Blacklist.Contains(unit, BlacklistFlags.Combat))
                {
                    Logging.Write("Current Kill POI is blacklisted. Clearing POI " + unit.SafeName + "!");
                    BotPoi.Clear();
                }
                else
                {
                    Logging.Write("Current target invalid. Switching to POI " + unit.SafeName + "!");
                    return unit;
                }
            }

            // now anything in the target list or a Player
            target = Targeting.Instance.TargetList
                .Where(
                    p => !Blacklist.Contains(p, BlacklistFlags.Combat)
                         && p.IsAlive
                // && p.DistanceSqr <= 40 * 40 // don't restrict check to 40 yds
                )
                .OrderBy(u => u.IsPlayer)
                .ThenBy(u => u.DistanceSqr)
                .FirstOrDefault();

            if (target != null)
            {
                // Return the closest one to us
                Logging.Write("Current target invalid. Switching to TargetList mob " + target.SafeName + "!");
                return target;
            }
            return null;
        }
        public static float SpellDistance(this WoWUnit unit, WoWUnit other = null)
        {
            // abort if mob null
            if (unit == null)
                return 99;

            // optional arg implying Me, then make sure not Mob also
            if (other == null)
                other = StyxWoW.Me;

            // pvp, then keep it close
            var dist = other.Location.Distance(unit.Location);
            dist -= other.CombatReach + unit.CombatReach;
            return Math.Max(0, dist);
        }
        public static int CalcDistancePriority(WoWUnit unit)
        {
            var prio = (int)SpellDistance(unit);
            if (prio <= 5)
                prio = 1;
            else if (prio <= 10)
                prio = 2;
            else if (prio <= 20)
                prio = 3;
            else
                prio = 4;
            return prio;
        }
        public static List<WoWPlayer> Tanks
        {
            get
            {
                if (!StyxWoW.Me.GroupInfo.IsInParty)
                    return new List<WoWPlayer>();
                return StyxWoW.Me.GroupInfo.RaidMembers.Where(p => p.HasRole(WoWPartyMember.GroupRole.Tank))
                    .Select(p => p.ToPlayer())
                    .Where(p => p != null).ToList();
            }
        }

        public static List<WoWPlayer> Healers
        {
            get
            {
                if (!StyxWoW.Me.GroupInfo.IsInParty)
                    return new List<WoWPlayer>();
                return StyxWoW.Me.GroupInfo.RaidMembers.Where(p => p.HasRole(WoWPartyMember.GroupRole.Healer))
                    .Select(p => p.ToPlayer()).Where(p => p != null).ToList();
            }
        }

        public static List<WoWPlayer> Dps
        {
            get
            {
                if (!StyxWoW.Me.GroupInfo.IsInParty)
                    return new List<WoWPlayer>();
                return
                    StyxWoW.Me.GroupInfo.RaidMembers.Where(
                        p => !p.HasRole(WoWPartyMember.GroupRole.Tank) && !p.HasRole(WoWPartyMember.GroupRole.Healer))
                        .Select(p => p.ToPlayer()).Where(p => p != null).ToList();
            }
        }

        public static bool MeIsTank
        {
            get
            {
                return (StyxWoW.Me.Role & WoWPartyMember.GroupRole.Tank) != 0 ||
                       Tanks.All(t => !t.IsAlive) && StyxWoW.Me.Shapeshift == ShapeshiftForm.Bear;
            }
        }
        [Flags]
    public enum WoWContext
    {
        None = 0,
        Normal = 0x1,
        Instances = 0x2,
        Battlegrounds = 0x4,

        All = Normal | Instances | Battlegrounds,
    }

        internal class Context
        {
            internal class KittyRoutine
            {
                internal static WoWContext CurrentWoWContext
                {
                    get
                    {
                        if (!StyxWoW.IsInGame)
                            return WoWContext.None;

                        var map = StyxWoW.Me.CurrentMap;

                        if (map.IsBattleground || map.IsArena)
                        {
                            return WoWContext.Battlegrounds;
                        }

                        return (map.IsDungeon || map.IsScenario || map.IsRaid) ? WoWContext.Instances : WoWContext.Normal;
                    }
                }
            }
        }
    }
}
